// Copyright 2013 wetalk authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package models

import (
	"fmt"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	"github.com/beego/i18n"

	"errors"
	"git.oschina.net/lijinchao2007/wetalk/modules/mongo"
	"git.oschina.net/lijinchao2007/wetalk/modules/utils"
	"git.oschina.net/lijinchao2007/wetalk/setting"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"reflect"
	"strings"
)

const TopicModelName = "topic"
const CategoryModelName = "category"

// post topic
type Topic struct {
	Id        int
	Id_       bson.ObjectId `bson:"_id"`
	Name      string        `orm:"size(30);unique"`
	Intro     string        `orm:"type(text)"`
	NameZhCn  string        `orm:"size(30);unique"`
	IntroZhCn string        `orm:"type(text)"`
	Image     *Image        `orm:"rel(one);null"`
	Slug      string        `orm:"size(100);unique"`
	Followers int           `orm:"index"`
	Order     int           `orm:"index"`
	Created   time.Time     `orm:"auto_now_add"`
	Updated   time.Time     `orm:"auto_now;index"`
}

func init() {
	beego.Info("reg topic cb")
	mongo.RegInitCb(TopicModelName, func() {
		beego.Info("topic init db callback ")
		index := mgo.Index{
			Key:        []string{"name"},
			Unique:     true,
			DropDups:   true,
			Background: true, // See notes.
			Sparse:     true,
		}

		mongo.GetCollection(TopicModelName).EnsureIndex(index)
		index = mgo.Index{
			Key:        []string{"namezhcn"},
			Unique:     true,
			DropDups:   true,
			Background: true, // See notes.
			Sparse:     true,
		}

		mongo.GetCollection(TopicModelName).EnsureIndex(index)
		index = mgo.Index{
			Key:        []string{"slug"},
			Unique:     true,
			DropDups:   true,
			Background: true, // See notes.
			Sparse:     true,
		}

		mongo.GetCollection(TopicModelName).EnsureIndex(index)

		index = mgo.Index{
			Key:        []string{"Name"},
			Unique:     true,
			DropDups:   true,
			Background: true, // See notes.
			Sparse:     true,
		}

		mongo.GetCollection(CategoryModelName).EnsureIndex(index)
		index = mgo.Index{
			Key:        []string{"slug"},
			Unique:     true,
			DropDups:   true,
			Background: true, // See notes.
			Sparse:     true,
		}

		mongo.GetCollection(CategoryModelName).EnsureIndex(index)
	})
}

func (m *Topic) Insert() error {
	beego.Info("insert topic ", m)
	m.Id_ = bson.NewObjectId()
	m.Created = time.Now()
	m.Updated = time.Now()
	if err := mongo.GetCollection(TopicModelName).Insert(m); err != nil {
		return err
	}
	return nil
}

func (m *Topic) Read(fields ...string) error {
	if len(fields) == 0 {
		if err := mongo.GetCollection(TopicModelName).Find(bson.M{"_id": m.Id_}).One(m); err != nil {
			return err
		}
	} else if len(fields) == 1 {
		v := reflect.ValueOf(m).Elem().FieldByName(fields[0])
		set := bson.M{strings.ToLower(fields[0]): v.String()}
		if err := mongo.GetCollection(TopicModelName).Find(set).One(m); err != nil {
			beego.Info("Read topic err ", err)
			return err
		}
	} else {
		return errors.New("read topic for to Much fields")
	}

	return nil
}

func (m *Topic) Update(fields ...string) error {
	cond := bson.M{"_id": m.Id_}
	m.Updated = time.Now()
	if err := mongo.GetCollection(TopicModelName).Update(cond, m); err != nil {
		return err
	}
	return nil
}

func (m *Topic) Delete() error {
	if err := mongo.GetCollection(TopicModelName).RemoveId(m.Id_); err != nil {
		return err
	}
	return nil
}

func (m *Topic) RefreshFollowers() int {
	cnt, err := FollowTopics().Filter("Topic", m.Id).Count()
	if err == nil {
		m.Followers = int(cnt)
		m.Update("Followers")
	}
	return m.Followers
}

func (m *Topic) String() string {
	return utils.ToStr(m.Id)
}

func (m *Topic) Link() string {
	return fmt.Sprintf("%stopic/%s", setting.AppUrl, m.Slug)
}

func (m *Topic) GetName(lang string) string {
	var name string
	switch i18n.IndexLang(lang) {
	case setting.LangZhCN:
		name = m.NameZhCn
	default:
		name = m.Name
	}
	return name
}

func (m *Topic) GetIntro(lang string) string {
	var intro string
	switch i18n.IndexLang(lang) {
	case setting.LangZhCN:
		intro = m.IntroZhCn
	default:
		intro = m.Intro
	}
	return intro
}

func Topics() orm.QuerySeter {
	return orm.NewOrm().QueryTable("topic").OrderBy("-Id")
}

// topic category
type Category struct {
	Id    int
	Id_   bson.ObjectId `bson:"_id"`
	Name  string        `orm:"size(30);unique"`
	Slug  string        `orm:"size(100);unique"`
	Order int           `orm:"index"`
}

func (m *Category) Insert() error {
	beego.Info("insert Category ", m)
	m.Id_ = bson.NewObjectId()
	if err := mongo.GetCollection(CategoryModelName).Insert(m); err != nil {
		return err
	}
	return nil
}

func (m *Category) Read(fields ...string) error {
	if len(fields) == 0 {
		if err := mongo.GetCollection(CategoryModelName).Find(bson.M{"_id": m.Id_}).One(m); err != nil {
			return err
		}
	} else if len(fields) == 1 {
		v := reflect.ValueOf(m).Elem().FieldByName(fields[0])
		set := bson.M{strings.ToLower(fields[0]): v.String()}
		if err := mongo.GetCollection(CategoryModelName).Find(set).One(m); err != nil {
			beego.Info("Read CategoryModelName err ", err)
			return err
		}
	} else {
		return errors.New("read CategoryModelName for to Much fields")
	}

	return nil
}

func (m *Category) Update(fields ...string) error {
	cond := bson.M{"_id": m.Id_}
	if err := mongo.GetCollection(CategoryModelName).Update(cond, m); err != nil {
		return err
	}
	return nil
}

func (m *Category) Delete() error {
	if err := mongo.GetCollection(CategoryModelName).RemoveId(m.Id_); err != nil {
		return err
	}
	return nil

}

func (m *Category) String() string {
	return utils.ToStr(m.Id)
}

func (m *Category) Link() string {
	return fmt.Sprintf("%scategory/%s", setting.AppUrl, m.Slug)
}

func Categories() orm.QuerySeter {
	return orm.NewOrm().QueryTable("category").OrderBy("-Id")
}

// user follow topics
type FollowTopic struct {
	Id      int
	User    *User     `orm:"rel(fk)"`
	Topic   *Topic    `orm:"rel(fk)"`
	Created time.Time `orm:"auto_now_add"`
}

func (*FollowTopic) TableUnique() [][]string {
	return [][]string{
		[]string{"User", "Topic"},
	}
}

func (m *FollowTopic) Insert() error {
	if _, err := orm.NewOrm().Insert(m); err != nil {
		return err
	}
	return nil
}

func (m *FollowTopic) Read(fields ...string) error {
	if err := orm.NewOrm().Read(m, fields...); err != nil {
		return err
	}
	return nil
}

func (m *FollowTopic) Update(fields ...string) error {
	if _, err := orm.NewOrm().Update(m, fields...); err != nil {
		return err
	}
	return nil
}

func (m *FollowTopic) Delete() error {
	if _, err := orm.NewOrm().Delete(m); err != nil {
		return err
	}
	return nil
}

func (m *FollowTopic) String() string {
	return utils.ToStr(m.Id)
}

func FollowTopics() orm.QuerySeter {
	return orm.NewOrm().QueryTable("follow_topic").OrderBy("-Id")
}

func init() {
	//	orm.RegisterModel(new(Topic), new(Category), new(FollowTopic))
}
